package com.efivestar.idm.api.service;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.efivestar.idm.api.common.Cons;
import com.efivestar.idm.api.exception.ErrorParamException;
import com.efivestar.idm.api.exception.NotSupportMethodHandlerException;
import com.efivestar.idm.api.service.lock.protocol.handler.LockProtocolHanderlIntf;
import com.efivestar.idm.api.util.ByteUtils;
import com.efivestar.idm.api.util.ExceptionUtil;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

@Component
public class SimpleHttpServer implements ApplicationContextAware {
	private static Logger logger = LoggerFactory.getLogger(SimpleHttpServer.class);
	
	@Value("${http.server.port:9080}")
    private int port;
    private ServerSocketChannel serverSocketChannel = null;
    private ExecutorService executorService;
    private static final int POOL_MULTIPLE = 4;
    
    private static Map<String, LockProtocolHanderlIntf> lockProtocolHanderlIntfMap;

    private int msgCnt = 0;

    private static String recvString = null;

    public static String getRecvString() {
        return recvString;
    }

    public void service() throws IOException {
    	logger.info("开启HTTP服务..， 服务端口 {}", port);
    	
        executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * POOL_MULTIPLE);
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().setReuseAddress(true);
        serverSocketChannel.socket().bind(new InetSocketAddress(this.port));
        
        while (true) {
            SocketChannel socketChannel = null;
            try {
                socketChannel = serverSocketChannel.accept();
                executorService.execute(new Handler(socketChannel));
            } catch (IOException e) {
                System.out.println(ExceptionUtil.getBriefExceptionStackTrace(e));
            }
        }
    }
    
    @PostConstruct
    public void startServer() {
        Thread t = new Thread(new Runnable() {
            public void run() {
                try {
                	IotService.subscribeNotification();
                    service();
                } catch (Exception e) {
                    logger.info("开启HTTP服务失败， 服务端口 {}", port);
                    logger.info(ExceptionUtil.getBriefExceptionStackTrace(e));
                }
            }
        });
        t.start();
    }
    
    @PreDestroy
    public void stop() throws IOException {
        logger.info("关闭HTTP服务");
        if (null == serverSocketChannel) {
            logger.error("server socket channel is null");
            return;
        }
        serverSocketChannel.close();
        serverSocketChannel = null;
        executorService.shutdownNow();
    }
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.lockProtocolHanderlIntfMap = applicationContext.getBeansOfType(LockProtocolHanderlIntf.class);
    }

    public static String recvBuf;

    public static String getRecvBuf() {
        return recvBuf;
    }

    public static void setRecvBuf(String recvBuf) {
        SimpleHttpServer.recvBuf = recvBuf;
    }

    class Handler implements Runnable {
        private SocketChannel socketChannel;

        public Handler(SocketChannel socketChannel) {
            this.socketChannel = socketChannel;
        }

        public void run() {
            handle(socketChannel);
        }

        private void handle(SocketChannel socketChannel) {
            try {
                ByteBuffer buffer = ByteBuffer.allocate(2048);
                socketChannel.read(buffer);
                buffer.flip();
                recvString = new String(buffer.array());

                // String responseHead = recvString.substring(0,
                // recvString.lastIndexOf("\n"));
                String responseJsonBody = "";

                if (recvString != null) {
                    try {
                    	int index = recvString.lastIndexOf("\n");
                    	if (index == -1) {
                    		responseJsonBody = recvString;
                    	} else {
                    		responseJsonBody = recvString.substring(index);
                    	}
                    } catch (Exception e) {
                        System.out.println("get responseJsonBody fail.");
                    }
                }

                System.out.print("接收到平台推送的第" + msgCnt + "消息 :");
                System.out.println(responseJsonBody);

                msgCnt++;
                System.out.println();
                
                // 适配消息处理器
                JSONObject json = JSON.parseObject(responseJsonBody);
                if (NotifyType.DEVICE_DATA_CHANGED.equals(json.getString("notifyType"))) {
                	String passthroughdata = json.getJSONObject("service").getJSONObject("data").getString("passthroughdata");
                	ByteBuf content = Unpooled.copiedBuffer(ByteUtils.hexToBytes(passthroughdata));
                    content.readerIndex(0);
                    int length = content.readableBytes();
                    byte bts[] = new byte[length];
                    content.getBytes(0, bts);
                    try {
                        byte [] head = new byte[2];
                        byte [] command = new byte[1];
                        content.getBytes(0, head, 0, 2);
                        //当智能锁发的包是ef02代表是远程开锁的回复，所以没有指令码.
                        if (!"ef02".equalsIgnoreCase(ByteUtils.bytes2hex(head))) {
                            content.getBytes(9, command, 0 , 1);
                        }
                        LockProtocolHanderlIntf protocolHandler = getProtocolHandler(head, command);
                        if (protocolHandler != null) {
                            byte[] executeResult = protocolHandler.execute(content);
                            if (executeResult == null) {
                                throw new NullPointerException();
                            }
                        } else {
                            // 没有能处理该消息的处理器
                            throw new NotSupportMethodHandlerException();
                        }
                    } catch (IndexOutOfBoundsException | NotSupportMethodHandlerException ex) {
                        if (ex instanceof  NotSupportMethodHandlerException) {
                            logger.error("找不到消息处理器:{}", ByteUtils.bytes2hex(bts));
                        } else if (ex instanceof  IndexOutOfBoundsException){
                            logger.error("数据包格式错误:{}", ByteUtils.bytes2hex(bts));
                        } else if (ex instanceof ErrorParamException){
                            logger.error("参数错误:{}", ByteUtils.bytes2hex(bts));
                        }
                    } catch (Exception ex) {
//                        ex.printStackTrace();

                        logger.error("信息处理返回null,指令码：{}， 异常：{}", new Object[]{ByteUtils.bytes2hex(bts), ex});
                    }
                }
            } catch (IOException e) {
                System.out.println(ExceptionUtil.getBriefExceptionStackTrace(e));
            } finally {
                try {
                    if (socketChannel != null)
                        socketChannel.close();
                } catch (IOException e) {
                    System.out.println(ExceptionUtil.getBriefExceptionStackTrace(e));
                }
            }
        }
        
        /**
         * 
        * @Title: getProtocolHandler
        * @Description: 根据具体协议返回对应的处理器
        * @author LiMiao
        * @date  2018年2月20日 下午2:02:20
        * @param head
        * @param command
        * @return
        * @throws
         */
        private LockProtocolHanderlIntf getProtocolHandler(byte []head, byte[]command){
            Set<Map.Entry<String, LockProtocolHanderlIntf>> entries = lockProtocolHanderlIntfMap.entrySet();
            for (Map.Entry<String, LockProtocolHanderlIntf> entry : entries) {
                LockProtocolHanderlIntf protocolHander = entry.getValue();
                if (protocolHander.match(head, command)){
                    return protocolHander;
                }
            }
            return null;
        }
    }
}
